#include "sfec.h"

// 1. Globals

// 1a. Vars

CSimpleCmdLine* pCmdLine=NULL;
COnlyOnce Can;

// y2009 cmd line params
int gBufSize = 0; // in msec
int gVolume = 50; // 0-100
HWND gCallerWindowHandle = 0;
int gNotifyPeriod = 3; // how often we should call PostMessage() - once every gNotifyPeriod seconds
int gNotifyMsgID = 2050; // notify message id
int gNotifyClientID = 1; // notify client id, 6-bit
int gDisplayStats = 0; // show stats: 1 - yes, 0 - no


// 1b. Functions

bool getval(const string s, int& val)
{
  istringstream is(s);
  is >> val;
  return !is.fail();
}

bool getval(const string s, int& val, int defval)
{
  bool res;
  val = defval;
  res = getval(s, val);
  if(!res) val = defval;
  return res;
}

int Saturate(int x, int lo, int hi)
{
  if (x<lo) x=lo; else
  if (x>hi) x=hi;
  return x;
}

// 1c. Classes

// ==== TheTime ====

TheTime::TheTime()
{
  Update();
}

void TheTime::Update()
{
#ifdef _WIN32_64_
  GetSystemTimeAsFileTime(&data);
#else
  data = time(NULL);
#endif
}

int TheTime::Difference(const TheTime& t0)
{
#ifdef _WIN32_64_
  double tt1 = data.dwHighDateTime*(33554432/78125.0) + data.dwLowDateTime/1e7;
  double tt0 = t0.data.dwHighDateTime*(33554432/78125.0) + t0.data.dwLowDateTime/1e7;
  return (int)(tt1-tt0);
#else
  return (int)(data-t0.data);
#endif
}

void TheTime::SetToPast(const int seconds)
{
  if (seconds==0) return;
#ifdef _WIN32_64_
  double tt1 = data.dwHighDateTime*(33554432/78125.0) + data.dwLowDateTime/1e7 - seconds;
  data.dwHighDateTime = (unsigned int)(tt1 / (33554432/78125.0));
  data.dwLowDateTime = (unsigned int)((tt1 - data.dwHighDateTime*(33554432/78125.0))*1e7);
#else
  data -= seconds;
#endif
}

bool TheTime::DateChanged(const TheTime& t0)
{
  int d1,d2;
#ifdef _WIN32_64_
  FILETIME localfiletime;
  SYSTEMTIME st;
  
  FileTimeToLocalFileTime(&data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);
  d1 = st.wDay;

  FileTimeToLocalFileTime(&t0.data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);
  d2 = st.wDay;
#else
  tm tt;
  localtime_r(&data, &tt);


  d1 = tt.tm_mday;

  localtime_r(&t0.data, &tt);
  d2 = tt.tm_mday;
#endif
  return d1!=d2;
}

int TheTime::GetDaySec(unsigned int& seconds)
{
#ifdef _WIN32_64_
  FILETIME localfiletime;
  SYSTEMTIME st;
  
  FileTimeToLocalFileTime(&data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);

  seconds = st.wHour*3600 + st.wMinute*60 + st.wSecond;
  return st.wDay;
#else
  tm tt;
  localtime_r(&data, &tt);

  seconds = tt.tm_hour*3600 + tt.tm_min*60 + tt.tm_sec;
  return tt.tm_mday;
#endif
}

bool TheTime::LoadFromString(string str)
{
 // Only YYYYMMDDxHHMMSS format supported
  int val;
#ifdef _WIN32_64_
  SYSTEMTIME st;
                                                 // 012345678901234
  if(!getval(str.substr(0,4),val)) return false; // YYYYmmddxhhmmss
  st.wYear = val;

  if(!getval(str.substr(4,2),val)) return false; // yyyyMMddxhhmmss
  st.wMonth = val;

  if(!getval(str.substr(6,2),val)) return false; // yyyymmDDxhhmmss
  st.wDay = val;

  if(!getval(str.substr(9,2),val)) return false; // yyyymmddxHHmmss
  st.wHour = val;

  if(!getval(str.substr(11,2),val)) return false; // yyyymmddxhhMMss
  st.wMinute = val;

  if(!getval(str.substr(13,2),val)) return false; // yyyymmddxhhmmSS
  st.wSecond = val;

  return SystemTimeToFileTime(&st, &data)!=0;
#else
  tm tt;
                                                 // 012345678901234
  if(!getval(str.substr(0,4),val)) return false; // YYYYmmddxhhmmss
  tt.tm_year = val-1900;

  if(!getval(str.substr(4,2),val)) return false; // yyyyMMddxhhmmss
  tt.tm_mon = val-1;

  if(!getval(str.substr(6,2),val)) return false; // yyyymmDDxhhmmss
  tt.tm_mday = val;

  if(!getval(str.substr(9,2),val)) return false; // yyyymmddxHHmmss
  tt.tm_hour = val;

  if(!getval(str.substr(11,2),val)) return false; // yyyymmddxhhMMss
  tt.tm_min = val;

  if(!getval(str.substr(13,2),val)) return false; // yyyymmddxhhmmSS
  tt.tm_sec = val;

  data = mktime(&tt);
#endif
}

string TheTime::SaveToString()
{
  // YYYYMMDDxHHMMSS format used
  char buf[128];
#ifdef _WIN32_64_
  SYSTEMTIME st;
  FILETIME localfiletime;
  
  FileTimeToLocalFileTime(&data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);

  sprintf(buf,"%04d%02d%02dx%02d%02d%02d", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
#else
  tm tt;
  localtime_r(&data, &tt);

  sprintf(buf,"%04d%02d%02dx%02d%02d%02d",tt.tm_year+1900, tt.tm_mon+1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec);
#endif
  return string(buf);
}

bool TheTime::SetSystemTime()
{
#ifdef _WIN32_64_
  SYSTEMTIME st;
  if(!FileTimeToSystemTime(&data, &st)) return false;

  return ::SetSystemTime(&st)!=0;
#else
  timeval tv;
  tv.tv_sec = data;
  tv.tv_usec = 0;

  return settimeofday(&tv, NULL)==0;
#endif
}


string TheTime::GetStr(bool format)
{
  char buf[128];
  string formatstr;

  if (format)
     formatstr = "[%04d.%02d.%02d %02d:%02d:%02d] ";
  else
     formatstr = "%04d.%02d.%02d %02d:%02d:%02d";
#ifdef _WIN32_64_
  FILETIME localfiletime;
  SYSTEMTIME st;
  
  FileTimeToLocalFileTime(&data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);

  sprintf(buf, (char*)formatstr.c_str(), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond);
#else
  tm tt;
  localtime_r(&data, &tt);

  sprintf(buf, formatstr.c_str(), tt.tm_year+1900, tt.tm_mon+1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec);
#endif
  return string(buf);
}

string TheTime::GetDateStr()
{
  char buf[64];
#ifdef _WIN32_64_
  FILETIME localfiletime;
  SYSTEMTIME st;
  
  FileTimeToLocalFileTime(&data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);

  sprintf(buf, "%04d%02d%02d", st.wYear, st.wMonth, st.wDay);
#else
  tm tt;
  localtime_r(&data, &tt);
  

  sprintf(buf, "%04d%02d%02d", tt.tm_year+1900, tt.tm_mon+1, tt.tm_mday);
#endif
  return string(buf);
}

string TheTime::GetTimeStr(string sep)
{
  char buf[64];
#ifdef _WIN32_64_
  FILETIME localfiletime;
  SYSTEMTIME st;
  
  FileTimeToLocalFileTime(&data,&localfiletime);
  FileTimeToSystemTime(&localfiletime,&st);

  sprintf(buf, "%02d%s%02d%s%02d", st.wHour, sep.c_str(), st.wMinute, sep.c_str(), st.wSecond);
#else
  tm tt;  
  localtime_r(&data, &tt);

  sprintf(buf, "%02d%s%02d%s%02d", tt.tm_hour, sep.c_str(), tt.tm_min, sep.c_str(), tt.tm_sec);
#endif
  return string(buf);
}


// === COnlyOnce ===

COnlyOnce::COnlyOnce()
{
  InitializeCriticalSection(&cs);
}

COnlyOnce::~COnlyOnce()
{
  DeleteCriticalSection(&cs);
}

bool COnlyOnce::Start(const string name, const int timeout)
{
  EnterCriticalSection(&cs);
    bool res;
    COnceItem oi;
    if(M.find(name)==M.end())
    {
      oi.Last = TheTime();
      oi.Name = name;
      M[name] = oi;
      res = true;
    } else
    {
      oi = M[name];
      TheTime now;
      if(now.Difference(oi.Last)>timeout)
      {
        oi.Last = now;
        M[name] = oi;
        res = true;
      } else res = false;
    }
  LeaveCriticalSection(&cs);
  return res;
}

void COnlyOnce::Update(const string name)
{
  EnterCriticalSection(&cs);
    COnceItem oi;
    oi.Last = TheTime();
    oi.Name = name;
    M[name] = oi;
  LeaveCriticalSection(&cs);
}

// == TheTimer ==

TheTimer::TheTimer(int aInterval) : Interval(aInterval), rc(0)
{
  Running = true; Enabled = false;

  hTick = CreateEvent(NULL, FALSE, FALSE, NULL);
  hThread = CreateThread(NULL, 0, ThreadFunc, this, 0, NULL);
  tid = timeSetEvent(Interval, 0, (LPTIMECALLBACK) hTick, 12345, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET);
}
TheTimer::~TheTimer()
{
  Stop();
  Running = false;
  timeKillEvent(tid);
  SetEvent(hTick);
  WaitForSingleObject(hThread, 500);
  CloseHandle(hThread);
  CloseHandle(hTick);
}
DWORD TheTimer::ThreadFunc(void* param)
{
  TheTimer* pTimer = (TheTimer*) param;
  unsigned int res = pTimer->Proc();
  return res;
}
unsigned int TheTimer::Proc()
{
  while (WaitForSingleObject(hTick, INFINITE)==WAIT_OBJECT_0)
  {
    if(!Running) break;
    if (Enabled)
    {
      Call();
      rc++;
    }
  }
  return 0;
}
void TheTimer::Call()
{
  printf("[%d] ",rc);
}
void TheTimer::Start()
{
  Enabled = true;
}
void TheTimer::Stop()
{
  Enabled = false;
}
void TheTimer::ComeOn()
{
  SetEvent(hTick);
}

// 2. FEC

// ====== CPacket ======

CPacket::CPacket(int aLength) : Length(aLength)
{
  pData = (unsigned char*) malloc((aLength+3)/4*4); // round up to nearest number divisible by 4
  Ready=rsEmpty;
}
CPacket::~CPacket()
{
  free(pData);
}
void CPacket::Load(unsigned char* pFrom)
{
  memcpy(pData, pFrom, Length);
  Ready=rsFull;
}
void CPacket::JustSave(unsigned char* pTo)
{
  memcpy(pTo, pData, Length);
}
void CPacket::Save(unsigned char* pTo)
{
  JustSave(pTo);
  DontNeedIt();
}
void CPacket::XorLoad(CPacket* pPacket1, CPacket* pPacket2)
{
  unsigned int
    *p1 = (unsigned int*) pPacket1->pData,
    *p2 = (unsigned int*) pPacket2->pData,
    *p3 = (unsigned int*) pData;
  for(int i=0; i<Length/4; i++) p3[i] = p1[i] ^ p2[i];
  Ready = rsFull;
}

// ====== CSimpleFEC ======

// == Commons ==

CSimpleFEC::CSimpleFEC(int aSamplesPerPacket, int aShuffleNo, int aBytesPerSample, int aRunLength, unsigned int aTag, unsigned int aTag2, bool aFec2x)
{
  Create(aSamplesPerPacket, aShuffleNo, aBytesPerSample, aRunLength, aTag, aTag2, aFec2x);
}

CSimpleFEC::CSimpleFEC(int aMaxBytes, int aShuffleNo, unsigned int aTag, unsigned int aTag2, bool aFec2x)
{
  int aSamplesPerPacket = (aMaxBytes-9)/33;
  if ((aSamplesPerPacket<=0) || (aSamplesPerPacket>10)) ErrorText="Max bytes per packet must be in [42; 339]"; else
  {
    if ( (aShuffleNo<1)||(aShuffleNo>11) ) ErrorText="Shuffle no must be in [1; 11]"; else
    {
      Create(aSamplesPerPacket, aShuffleNo, 33, 255, aTag, aTag2, aFec2x);
      return;
    }
  }
  Create(5, 3, 33, 255, aTag, aTag2, aFec2x);
}

void CSimpleFEC::Create(int aSamplesPerPacket, int aShuffleNo, int aBytesPerSample, int aRunLength, unsigned int aTag, unsigned int aTag2, bool aFec2x)
{
  SamplesPerPacket = aSamplesPerPacket; ShuffleNo = aShuffleNo; BytesPerSample = aBytesPerSample; Fec2x = aFec2x;
  RunLength=aRunLength; Tag = aTag; Tag2=aTag2;

  iRunLength=0;
  iNext=0;
  pTmp = (unsigned char*) malloc(SamplesPerPacket*BytesPerSample); iTmp = 0;

  if (aFec2x)
  {
    ShuffleNo = 1;
    nPackets = ShuffleNo;
    nParityPackets = ShuffleNo;
    Tag2 = Tag;

  } else
  {
    nPackets = ShuffleNo * 2;
    nParityPackets = ShuffleNo;
  }
  iPacket=0;

  int i,j,c=0;

  for(i=0; i<nPackets; i++) pPackets.push_back(new CPacket(SamplesPerPacket*BytesPerSample));
  for(i=0; i<nParityPackets; i++) pParityPackets.push_back(new CPacket(SamplesPerPacket*BytesPerSample));

//  TimeLag = 20*2*SamplesPerPacket*ShuffleNo; // in msec
//  PacketRate = 75.0/SamplesPerPacket; // in packets per sec

  // decoding stuff
  for(i=0; i<RunLength; i++) pRPackets.push_back(new CPacket(BytesPerSample));
  iWait=-1; MaxWait = /*11*10*2*/ 6*11*2; LastRL=-1; Lastn=1; LastExport=RunLength-1;
  DetectedDelta = -1;
  ClearStats(); nSame=0;
  pSavior = new CPacket(BytesPerSample);
}


CSimpleFEC::~CSimpleFEC()
{
  int i;
  for(i=0; i<nPackets; i++) delete pPackets[i];
  for(i=0; i<nParityPackets; i++) delete pParityPackets[i];
  for(i=0; i<RunLength; i++) delete pRPackets[i];
  while (MoreSamples.size()>0)
  {
    delete MoreSamples.front();
    MoreSamples.pop();
  }
  delete pSavior;
  free(pTmp);
}

int CSimpleFEC::GetInterval()
{
  // here we assume one sample is 0.02 sec, otherwise single sample time interval must be added as a parameter (to constructor)
  if (Fec2x)
    return (int) (20.0 * SamplesPerPacket * 2/4 * 0.98); // slightly less
  else
    return (int) (20.0 * SamplesPerPacket * 2/3 * 0.98); // slightly less
}

int CSimpleFEC::GetMaxQueue()
{
  if (Fec2x)
    return ShuffleNo * 3;
  else
    return ShuffleNo * 3 * 2;
}

unsigned int CSimpleFEC::SwapOrder4(unsigned int x)
{
  return SwapOrder2((unsigned short)(x>>16))^(SwapOrder2((unsigned short)x)<<16);
}

unsigned short CSimpleFEC::SwapOrder2(unsigned short x)
{
  return (x>>8)^(x<<8);
}

// == Encoding ==

void CSimpleFEC::AddSample(unsigned char* inData)
{
  if (iPacket==nPackets) iPacket=0; // Clear
  memcpy(&pTmp[iTmp*BytesPerSample], inData, BytesPerSample);
  iTmp++;
  if (iTmp==SamplesPerPacket) EncProcessPacket();
}

void CSimpleFEC::AddSamples(unsigned char* inData, int Length)
{
  int i, have = Length/BytesPerSample; // assume >=1

  if (MoreSamples.size()>0) EatSample(); else
  {
    AddSample(inData);
    have--;
    inData+=BytesPerSample;
  }
  for(i=0; i<have; i++)
  {
    CPacket* p = new CPacket(BytesPerSample);
    p->Load(&inData[i*BytesPerSample]);
    MoreSamples.push(p);
  }
}

void CSimpleFEC::EatSample()
{
  if (MoreSamples.size()>0)
  {
    AddSample(MoreSamples.front()->pData);
    delete MoreSamples.front();
    MoreSamples.pop();
  }
}

void CSimpleFEC::EncProcessPacket()
{
  pPackets[iPacket]->Load(pTmp);
  iPacket++;
  if (iPacket==nPackets) EncProcessParity();
  iTmp=0;
}

void CSimpleFEC::EncProcessParity()
{
  if (Fec2x)
    for (int i=0; i<ShuffleNo; i++) pParityPackets[i]=pPackets[i];
  else
    for (int i=0; i<ShuffleNo; i++) pParityPackets[i]->XorLoad(pPackets[i], pPackets[i+ShuffleNo]);
}

bool CSimpleFEC::GetPacket(unsigned char* outData, int* outSize)
{
  int ptr=0;
  while (true)
  {
    if(iNext<nPackets) // normal packets
    { 
      if(pPackets[iNext]->Ready==rsFull)
      {
        memcpy(&outData[ptr], &Tag, 4); ptr+=4;
        unsigned char RL = (iRunLength+iNext*SamplesPerPacket)%RunLength; outData[ptr] = RL; ptr++;
        unsigned int PL = pPackets[iNext]->Length; memcpy(&outData[ptr], &PL, 4); ptr+=4;
        pPackets[iNext]->Save(&outData[ptr]); ptr+=pPackets[iNext]->Length;
        *outSize = ptr;
        iNext++;
        break;
      }
      if (MoreSamples.size()>0)
      {
        EatSample();
        return GetPacket(outData, outSize);
      } else return false;
    } else
    if(iNext<nPackets+nParityPackets) // parity packets
    {
      int idx = iNext - nPackets;
      memcpy(&outData[ptr], &Tag2, 4); ptr+=4;
      unsigned char RL1 = (iRunLength+           idx *SamplesPerPacket)%RunLength; outData[ptr] = RL1; ptr++;
      if (!Fec2x) {
        unsigned char RL2 = (iRunLength+(ShuffleNo+idx)*SamplesPerPacket)%RunLength; outData[ptr] = RL2; ptr++;
      }
      unsigned short PL = pParityPackets[idx]->Length; memcpy(&outData[ptr], &PL, 2); ptr+=2;
      pParityPackets[idx]->Save(&outData[ptr]); ptr+=pPackets[idx]->Length;
      *outSize = ptr;
      iNext++;
      break;
    } else // can't be !
    {
      iNext=0; 
      return false;
    }
    break; // can't be
  }
  if (iNext==nPackets+nParityPackets)
  {
    iNext=0;
    iRunLength = (iRunLength+nPackets*SamplesPerPacket)%RunLength;
  }
  return true;
}

// == Decoding ==

//#define mydebug

//* debug begin
/*#include <string>
using namespace std;
class CMyDebug { public:
  FILE* flog;
  CMyDebug(string fname) {flog = fopen(fname.c_str(), "wt");};
  string inttostr(int n){char buf[32]; sprintf(buf, "%d", n); return string(buf);}
  void printsome(string title, unsigned char* buf, int n, int atmost) {
    int i; unsigned char c; if (atmost<n) n=atmost; fprintf(flog,"%s: ",title.c_str());
    for(i=0; i<n; i++) fprintf(flog,"%4d", buf[i]); fprintf(flog," (");
    for(i=0; i<n; i++) {c=buf[i]; if (c<32) c='_'; fprintf(flog,"%c",c);}
    fprintf(flog,")\n"); fflush(flog);};
  void printstr(string s) {fprintf(flog,"%s\n",s.c_str());fflush(flog);}
  void printstr0(string s) {fprintf(flog,"%s",s.c_str());fflush(flog);}
  void printint(int x) {fprintf(flog,"%u\n",x);fflush(flog);}
  void printstrint(string s, int x) {fprintf(flog,"%s: %u\n", s.c_str(), x);fflush(flog);}
  ~CMyDebug(){fclose(flog);};
};*/
#ifdef mydebug
CMyDebug md1("debug_fec.log", false);
#endif
// debug end*/





void CSimpleFEC::AddNormalPacket(unsigned char* inData, int Length)
{
  int ptr=0, i,m;
  nReceived++;
  int RL = inData[ptr]%RunLength; ptr++;
  int PL; memcpy(&PL, &inData[ptr], 4); ptr+=4;
#ifdef mydebug
md1.printstr("received RL: "+md1.inttostr(RL));
#endif

  int meat = Length-ptr; // 165
  int n = meat/BytesPerSample; // 5
  Lastn = n;

  if ( RL== LastRL )
  {
    nSame++;
    return;
  }

  if(LastRL!=-1)
  {
    signed int dLost = ((RL-LastRL-n+2*RunLength+RunLength / 2) % RunLength) - RunLength / 2;
    // dont need oldies
    if (dLost>=0)
    {
      i=(LastRL+RunLength-MaxWait)%RunLength;
      m=(RL+RunLength-MaxWait)%RunLength;
#ifdef mydebug
md1.printstr("dont need: "+md1.inttostr(i)+".."+md1.inttostr(m-1));
#endif
      while (i!=m)
      {
        pRPackets[i]->DontNeedIt();
        i = (i+1)%RunLength;
      }
    }
#ifdef mydebug
md1.printstr("nLost += "+md1.inttostr(dLost));
#endif
    nLost += dLost/n;
  }
  for(i=0; i<n; i++)
  {
    if (pRPackets[(RL+i)%RunLength]->Ready!=rsUsed)
    {
      pRPackets[(RL+i)%RunLength]->Load(&inData[ptr]); ptr+=BytesPerSample;
      NewOnes.push_back((RL+i)%RunLength);
    }
#ifdef mydebug
md1.printstr0("["+md1.inttostr((RL+i)%RunLength)+"] ");
#endif
  }
#ifdef mydebug
  md1.printstr("");
#endif
  LastRL = RL;
}
void CSimpleFEC::AddParityPacket(unsigned char* inData, int Length)
{
  int ptr=0, i,m;
  nReceivedP++;
  int RL1 = inData[ptr]%RunLength; ptr++;
  int RL2 = inData[ptr]%RunLength; ptr++;
  short PL; memcpy(&PL, &inData[ptr], 2); ptr+=2;
  int n = (Length-ptr)/BytesPerSample;
  int canrep=0, cantrep=0;

  DetectedDelta = (RL2-RL1+RunLength)%RunLength;

//md1.printstr("n="+md1.inttostr(n)+" / rl1="+md1.inttostr(RL1)+" / rl2="+md1.inttostr(RL2)+" / "+md1.inttostr(PL));

#ifdef mydebug
  string s0=md1.inttostr(RL1)+"-"+md1.inttostr(RL2)+": ";
#endif
  for(i=0; i<n; i++)
  {
#ifdef mydebug
if(pRPackets[RL1]->Ready!=rsEmpty) s0+="1-"; else s0+="0-";
if(pRPackets[RL2]->Ready!=rsEmpty) s0+="1 "; else s0+="0 ";
#endif
    if ( pRPackets[RL1]->Ready + pRPackets[RL2]->Ready == 1)
    {
      // hooray! gimmie, gimmie
      pSavior->Load(&inData[ptr]);
      int rep=RL2, use=RL1;
      if(pRPackets[RL1]->Ready==rsEmpty) {rep = RL1; use = RL2;}
      pRPackets[rep]->XorLoad(pRPackets[use], pSavior);
//      NewOnes.push(rep);
      NewOnes.push_back(rep);
      pRPackets[rep]->Ready = rsUsed;
#ifdef mydebug
md1.printstr0("["+md1.inttostr(rep)+"] ");
#endif
      canrep++;
    } else
    if (pRPackets[RL1]->Ready==rsEmpty) cantrep++;
    ptr+=BytesPerSample;
    RL1 = (RL1+1)%RunLength;
    RL2 = (RL2+1)%RunLength;
  }
#ifdef mydebug
  if(canrep+cantrep!=0) md1.printstr(" R!"); else md1.printstr(" "+s0);
#endif
  nNotRepaired+=cantrep/n;
  nRepaired+=canrep/n;
}

void CSimpleFEC::AddOtherPacket(unsigned char* inData, int Length)
{
  nReceivedU++;
}

bool CSimpleFEC::GetSample(unsigned char* outData, unsigned char* outNo)
{
  if (NewOnes.size()>0)
  {
    *outNo = NewOnes.front();
    NewOnes.erase(NewOnes.begin());
    pRPackets[*outNo]->JustSave(outData);
    LastExport = *outNo;
    return true;
  } else return false;
}

bool CSimpleFEC::GetSampleDelayed(unsigned char* outData, unsigned char* outNo, int delay)
{
  int i;
  if (delay==-1)
    if (DetectedDelta!=-1)
      delay = DetectedDelta*2+1;
    else
      delay = 3;


  if (NewOnes.size()>0)
  { //             0         7     100      255         255 = -93
    int maxid = (LastRL+Lastn-1 - delay + RunLength)%RunLength;
    int gotit = -1;
    int have;
    int minf=RunLength;
    for(i=0; i<NewOnes.size(); i++)
      // loop for a those samples behind the delay
//      if ( (have=(NewOnes[i] - maxid + RunLength*2 -MaxWait)%RunLength) <= RunLength-MaxWait)
      if ( (have=(NewOnes[i] - maxid + RunLength +MaxWait)%RunLength) <= MaxWait)
      {
        // check if it is too late for the sample (then kick it)
//        if ( (NewOnes[i] + RunLength*2 -MaxWait)%RunLength<= (LastExport +RunLength*2-MaxWait)%RunLength)
        if ( (LastExport-NewOnes[i] + RunLength +MaxWait)%RunLength >= MaxWait )
        {
#ifdef mydebug
  md1.printstr("Too late, KICK: [-"+md1.inttostr(NewOnes[i])+"]");
  md1.printstr("why? ("+md1.inttostr(NewOnes[i]) +", "+ md1.inttostr(LastExport)+")");
#endif
          NewOnes.erase(NewOnes.begin()+i);
          i--;
          nTooLate++;
          continue;
        } else
        // searching for a minimum of sample indeces
        if (have<minf)
        {
          minf = have;
          gotit = i;
        }
      }

#ifdef mydebug
string tmp=md1.inttostr(maxid)+" of "+md1.inttostr(NewOnes.size())+": ";
for(i=0; i<NewOnes.size(); i++)
  if (i==gotit)
    tmp+= "["+md1.inttostr(NewOnes[i])+"*] ";
  else
    tmp+= "["+md1.inttostr(NewOnes[i])+"] ";
md1.printstr(tmp);
#endif


    if (gotit==-1) return false;
    *outNo = NewOnes[gotit];
    LastExport = *outNo;

    NewOnes.erase(NewOnes.begin()+gotit);

    pRPackets[*outNo]->JustSave(outData);
    return true;
  } else return false;
}

bool CSimpleFEC::IsRecent(int ID)
{
  return pRPackets[ID]->Ready!=rsEmpty;
}